///PHILOTES Source Code.  (C)2012 PhiloLabs
#pragma once

#include "ExtrasPrerequisites.h "

#include "OgrePrerequisites.h"
#include "OgreSingleton.h"
#include "OgreStringVector.h"
#include "Overlay.h"
#include "OgreScriptLoader.h"
#include "OgreFrustum.h"

namespace Extras {

    class _PhiloExtrasExport OverlayManager : public Ogre::Singleton<OverlayManager>, public ScriptLoader, public MemAlloc_SceneObj
    {
    public:
        typedef map<Ogre::String, Overlay*>::type OverlayMap;
		typedef map<Ogre::String, OverlayElement*>::type ElementMap;
		typedef map<Ogre::String, OverlayElementFactory*>::type FactoryMap;
    protected:
        OverlayMap mOverlayMap;
        StringVector mScriptPatterns;

        void parseNewElement( DataStreamPtr& chunk, Ogre::String& elemType, Ogre::String& elemName, 
            bool isContainer, Overlay* pOverlay, bool isTemplate, Ogre::String templateName = Ogre::String(""), OverlayContainer* container = 0);
        void parseAttrib( const Ogre::String& line, Overlay* pOverlay);
        void parseElementAttrib( const Ogre::String& line, Overlay* pOverlay, OverlayElement* pElement );
        void skipToNextCloseBrace(DataStreamPtr& chunk);
        void skipToNextOpenBrace(DataStreamPtr& chunk);
        
        int mLastViewportWidth, mLastViewportHeight;
        bool mViewportDimensionsChanged;
        OrientationMode mLastViewportOrientationMode;

	    bool parseChildren( DataStreamPtr& chunk, const Ogre::String& line,
            Overlay* pOverlay, bool isTemplate, OverlayContainer* parent = NULL);

		FactoryMap mFactories;

		ElementMap mInstances;
		ElementMap mTemplates;

		typedef set<Ogre::String>::type LoadedScripts;
		LoadedScripts mLoadedScripts;




		ElementMap& getElementMap(bool isTemplate);

		OverlayElement* createOverlayElementImpl(const Ogre::String& typeName, const Ogre::String& instanceName, ElementMap& elementMap);

		OverlayElement* getOverlayElementImpl(const Ogre::String& name, ElementMap& elementMap);
		
		bool hasOverlayElementImpl(const Ogre::String& name, ElementMap& elementMap);

		void destroyOverlayElementImpl(const Ogre::String& instanceName, ElementMap& elementMap);

		void destroyOverlayElementImpl(OverlayElement* pInstance, ElementMap& elementMap);

		void destroyAllOverlayElementsImpl(ElementMap& elementMap);

    public:
        OverlayManager();
        virtual ~OverlayManager();

        /// @copydoc ScriptLoader::getScriptPatterns
        const StringVector& getScriptPatterns(void) const;
        /// @copydoc ScriptLoader::parseScript
        void parseScript(DataStreamPtr& stream, const Ogre::String& groupName);
        /// @copydoc ScriptLoader::getLoadingOrder
        Real getLoadingOrder(void) const;

        
        Overlay* create(const Ogre::String& name);
        
        Overlay* getByName(const Ogre::String& name);
        
        void destroy(const Ogre::String& name);
        
        void destroy(Overlay* overlay);
        
        void destroyAll(void);
        typedef MapIterator<OverlayMap> OverlayMapIterator;
        OverlayMapIterator getOverlayIterator(void);

        
        void _queueOverlaysForRendering(Camera* cam, RenderQueue* pQueue, Viewport *vp);

        
        bool hasViewportChanged(void) const;

        
        int getViewportHeight(void) const;
        
        
        int getViewportWidth(void) const;
        Real getViewportAspectRatio(void) const;

        
        OrientationMode getViewportOrientationMode(void) const;

		
		OverlayElement* createOverlayElement(const Ogre::String& typeName, const Ogre::String& instanceName, bool isTemplate = false);

		
		OverlayElement* getOverlayElement(const Ogre::String& name, bool isTemplate = false);

		
		bool hasOverlayElement(const Ogre::String& name, bool isTemplate = false);
		
		
		void destroyOverlayElement(const Ogre::String& instanceName, bool isTemplate = false);

		
		void destroyOverlayElement(OverlayElement* pInstance, bool isTemplate = false);

		
		void destroyAllOverlayElements(bool isTemplate = false);

		
		void addOverlayElementFactory(OverlayElementFactory* elemFactory);
		
		
		const FactoryMap& getOverlayElementFactoryMap() const {
			return mFactories;
		}

		OverlayElement* createOverlayElementFromTemplate(const Ogre::String& templateName, const Ogre::String& typeName, const Ogre::String& instanceName, bool isTemplate = false);
		/**
		*  @remarks
		*  Creates a new OverlayElement object from the specified template name.  The new
		*  object's name, and all of it's children, will be instanceName/orignalName.
		*/
		OverlayElement* cloneOverlayElementFromTemplate(const Ogre::String& templateName, const Ogre::String& instanceName);

		OverlayElement* createOverlayElementFromFactory(const Ogre::String& typeName, const Ogre::String& instanceName);

		typedef MapIterator<ElementMap> TemplateIterator;
		
		TemplateIterator getTemplateIterator ()
		{
			return TemplateIterator (mTemplates.begin (), mTemplates.end ()) ;
		}
		/* Returns whether the Element with the given name is a Template */
		bool isTemplate (Ogre::String strName) const {
			return (mTemplates.find (strName) != mTemplates.end()) ;
		}


        
        static OverlayManager& getSingleton(void);
        
        static OverlayManager* getSingletonPtr(void);
    };


}
